package com.yuntsg.subject_database.util;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.SecureUtil;
import lombok.Data;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 专科数据库工具类
 */
public class SubDataUtils {

    // cell hand "cancer" mouth (android ios) wapo skdn "there are three cup" 尹春天 [au] is no plane[TI] kind
    // 从文本中解析出标准词，并返回解析不出来的字符串。"2356-6140 Other Topics \"zhou, junbo\" 2013[py]" -> remaining:other topics  [FieldAndValue(field=sn, value=2356-6140), FieldAndValue(field=[py], value=2013), FieldAndValue(field=[au], value=zhou, junbo)]
    public static String doExtractField(String freeWords, Map<String, Set<StandardNameAndType>> nameAndTypeMap, Set<FieldAndValue> extractFieldSet, String end, String field) {
        System.out.println("\033[31;1m" + "~~~doExtract~~~freeWords: " + freeWords + "\033[0m");
        StringBuilder remaining = new StringBuilder();
        if (null != freeWords && freeWords.length() > 1) {
            freeWords = reRemaining(freeWords);
            String spl = " ";
            if (OftenUtils.isContainChinese(freeWords.replace("纃", ""))) {
                spl = "";
            }
            System.out.println("freeWords: " + freeWords);
            int splLen = spl.length();
            List<String> list = Arrays.asList(freeWords.split(spl));
            int iLen = 0;
            int listLen = list.size();
            for (int i = 0; i < listLen; i++) {
                if (freeWords.length() >= iLen) {
                    String iStr = freeWords.substring(iLen);
                    iLen += list.get(i).length() + splLen;
                    int jLen = 0;
                    for (int j = listLen - 1; j >= 0; j--) {
                        if (iStr.length() - jLen >= 0) {
                            String jStr = iStr.substring(0, iStr.length() - jLen);
                            jLen += list.get(j).length() + splLen;
                            String md5Name = "";
                            if (jStr.length() > 16) {
                                md5Name = SecureUtil.md5(jStr).substring(8, 24).toLowerCase();
                            }
                            if (nameAndTypeMap.containsKey(jStr) || (md5Name.length() > 0 && nameAndTypeMap.containsKey(md5Name))) {
                                System.out.println("----jStr: " + jStr);
                                Set<StandardNameAndType> standardNameAndTypes = new HashSet<>();
                                if (jStr.length() > 16) {
                                    standardNameAndTypes = nameAndTypeMap.get(md5Name);
                                } else {
                                    standardNameAndTypes = nameAndTypeMap.get(jStr);
                                }
                                for (StandardNameAndType standardNameAndType : standardNameAndTypes) {
                                    FieldAndValue fieldAndValue = new FieldAndValue();
                                    String standard = getStandardName(standardNameAndType.getStandard(), jStr);
                                    fieldAndValue.setValue(standard);
                                    // 因为pmid号是加入map的，如果匹配的字符串是一个数字，且为2000-2025之间，则认为是出版年，不是pmid号。如果是一个小于10000的数字，则认为是标题，其它才认为是pmid号。如果提取的文本的结尾，与原来的字段限定的结尾相同，则一定按原来的字段限定执行。
                                    String tempField = standardNameAndType.getType();
                                    if (NumberUtil.isInteger(jStr)) {
                                        int num = Integer.parseInt(jStr);
                                        if (num > 2000 && num < 2025) {
                                            tempField = "py";
                                        } else {
                                            if (num < 10000) {
                                                tempField = "ti";
                                            } else {
                                                tempField = standardNameAndType.getType();
                                            }
                                        }
                                    }
                                    if (jStr.endsWith(end) && end.length() > 1) {
                                        tempField = field;
                                    }
                                    System.out.println("tempField: " + tempField);
                                    fieldAndValue.setField(tempField);
                                    extractFieldSet.add(fieldAndValue);
                                }
                                iLen += (jStr.length() - list.get(i).length());
                                i = i + jStr.split(spl).length - 1;
                                break;
                            }
                            if (jStr.split(spl).length == 1) {
                                remaining.append(jStr).append(spl);
                            }
                        }
                    }
                }
            }
        }
        System.out.println("extractFieldSet: " + extractFieldSet + " remaining: " + resetRemaining(remaining.toString()));
        return resetRemaining(remaining.toString());
    }

    // 切割前的准备，对字段限定前面的文本，如果可以切割为期刊、作者等，进行切割，如果带有引号，则引号不切割，直接作为字段限定返回。如果只有一个引号，则整个都作为字段限定返回。
    public static String prepareExtractField(String value, String field, Set<FieldAndValue> extractFieldSet, Map<String, Set<StandardNameAndType>> nameAndTypeMap) {
        System.out.println("\033[31;1m" + "~~~prepareExtractField~~~value: " + value + "\033[0m");
        value = value.replaceAll(" +", " ").trim();

        // 如果字段限定是期刊，则认为限定前的字符串，全部是期刊的名称。
        if (field.equals("ta") || field.equals("[ta]")) {
            FieldAndValue fieldAndValue = new FieldAndValue();
            fieldAndValue.setField(field);
            fieldAndValue.setValue(value);
            extractFieldSet.add(fieldAndValue);
            return "";
        }
        if (!value.contains(" ")) {
            FieldAndValue fieldAndValue = new FieldAndValue();
            fieldAndValue.setField(field);
            fieldAndValue.setValue(value);
            extractFieldSet.add(fieldAndValue);
            return "";
        }
        String[] split = value.split(" ");
        String end = split[split.length - 1];
        // 解决非小细胞癌的问题
        if (value.endsWith(" non " + end)) {
            end = " non " + end;
        }
        if (value.endsWith(" not " + end)) {
            end = " not " + end;
        }
        if (value.endsWith(" no " + end)) {
            end = " no " + end;
        }
        // 如果字段限定前面的文本中含有引号，如果是[ti]字段，则仍按[ti]输出，如果不是，则按[ts]输出。原来带的引号先替换为"⟪"和"⟫"，执行检索时仍按引号进行检索。如果引号内的文本结尾就是整个文本的结尾，则按原来的field输出。
        System.out.println("value:a " + value);
        Matcher matcher = Pattern.compile("\"([^\"]+)\"").matcher(value);
        while (matcher.find()) {
            String group = matcher.group(1);
            String temp = "\"" + group.replace("(", "⟪").replace(")", "⟫").replace("⬆and⬇", " and ").replace("⬆oor⬇", " or ").replace("⬆not⬇", " not ") + "\"";
            FieldAndValue fieldAndValue = new FieldAndValue();
            if (field.equals("ti") || field.equals("[ti]")) {
                fieldAndValue.setField("ti");
            } else if (value.endsWith("\"" + group + "\"")) {
                fieldAndValue.setField(field);
            } else {
                fieldAndValue.setField("ts");
            }
            if (group.endsWith(end)) {
                fieldAndValue.setField(field);
            }
            fieldAndValue.setValue(temp);
            extractFieldSet.add(fieldAndValue);
            value = value.replace("\"" + group + "\"", "");
        }

        // 如果限定字段前是一个括号，则把括号内的内容作为整体提取出来，作为字段的值，比如:wapo skdn (there are three cup) [ti] -> field=[ti], value=there are three cup  remaining: wapo skdn 括号前的字符串，中文的不知道有多长，英文的也无法判断，不如直接给自由文本去提取去。
        if (value.endsWith(")")) {
            String[] list = value.split("");
            int len = list.length - 1;
            for (int i = len; i > 0; i--) {
                if (list[i].equals("(")) {
                    FieldAndValue fieldAndValue = new FieldAndValue();
                    fieldAndValue.setField(field);
                    fieldAndValue.setValue(value.substring(i + "(".length(), value.length() - ")".length()));
                    extractFieldSet.add(fieldAndValue);
                    value = value.substring(0, i);
                    break;
                }
            }
        }

        // 如果不是引号，则全部作为余下的，再作两头卡的分解，最后余下的返回。如果余下的结尾与原来的结尾是一致的，则将最后的这个结尾加上原来的字段限定，并将余下的把这个结尾给予去除。如果提取的文本的结尾，与原来的字段限定的结尾相同，则一定按原来的字段限定执行。
        String remaining = doExtractField(value, nameAndTypeMap, extractFieldSet, end, field);
        System.out.println("value:b |" + value + "| end: |" + end + "| field: |" + field + "| remaining: |" + remaining + "|");
        if (remaining.endsWith(end) || value.equals(end)) {
            System.out.println("value:c |" + value + "| end: |" + end + "| field: |" + field + "| remaining: |" + remaining + "|");
            FieldAndValue fieldAndValue = new FieldAndValue();
            fieldAndValue.setField(field);
            fieldAndValue.setValue(end);
            extractFieldSet.add(fieldAndValue);
            if (remaining.length() >= end.length()) {
                System.out.println("value:d |" + value + "| end: |" + end + "| field: |" + field + "| remaining: |" + remaining + "|");
                remaining = remaining.substring(0, remaining.length() - end.length());
            }
        }
        System.out.println("value:e |" + value + "| end: |" + end + "| field: |" + field + "| remaining: |" + remaining + "|");
        return remaining;
    }

    // 对字段限定前的字符串，进行标准字段提取，如果可以匹配到标准的字段，存入extractFieldSet，如果匹配不到标准的字段，认为是自由文本，给予返回。本方法还包括prepareExtractField（准备提取）和doExtractField（执行提取）两部分。
    public static String fieldFrontExtract(String search, Set<String> fieldSet, Set<FieldAndValue> extractFieldSet, Map<String, Set<StandardNameAndType>> nameAndTypeMap) {
        System.out.println("\033[31;1m" + "~~~fieldFrontExtract~~~search: " + search + "\033[0m");
        StringBuilder result = new StringBuilder();
        StringBuilder remaining = new StringBuilder();
        String[] list = search.split("");
        int end = 0;
        int len = list.length;
        for (int i = 0; i < len; i++) {
            for (int j = 3; j < 8; j++) {
                int k = j + 1;
                if (list[i].equals("[") && len > i + j && list[i + j].equals("]")) { // [au]、[cau]、[cnau]、[cnfau]、[cnunit]
                    String save = search.substring(end, i + k);
                    remaining.append(save);
                    String field = search.substring(i, i + k);
                    String value = search.substring(end, i);
                    if (fieldSet.contains(field)) {
                        save = prepareExtractField(value, field, extractFieldSet, nameAndTypeMap);
                        result.append(changeStr(save));
                    } else {
                        result.append(save.replace("(", "↱").replace(")", "↵"));
                    }
                    i = i + k;
                    end = i;
                    if (end >= len) {
                        break;
                    }
                }
            }
        }
        if (search.length() > remaining.toString().length()) {
            result.append(search.substring(remaining.toString().length()));
        }
        System.out.println("result: " + result.toString().replace("↱", "(").replace("↵", ")").replaceAll(" +", " ").trim() + " extractFieldSet: " + extractFieldSet);
        // 对于限定检索前出现的括号，如果是某个字段内的标准名称，分别替换为"⟪"，"⟫"，对于不是的，程序为了避免后续被意外替换，暂时给替换成了"↱↵"，但在实际输出时，这些不是的还是要再次替换回来，以保持原样，使其仍然可以对括号进行检索。
        return result.toString().replace("↱", "(").replace("↵", ")").replaceAll(" +", " ").trim();
    }

    // 正则取最内层括号内的内容，然后将括号替换为"⟪"，"⟫"。
    public static String changeStr(String str) {
        if (null != str && str.length() > 1) {
            while (true) {
                Matcher matcher = Pattern.compile("\\(([^\\(\\)]+)\\)").matcher(str);
                if (matcher.find()) {
                    String group = matcher.group(1);
                    str = str.replace("(" + group + ")", "⟪" + group + "⟫");
                } else {
                    break;
                }
            }
            return str;
        }
        return "";
    }

    // 原始字符串中，" AND "、" OR "、" NOT "分别替换为"⬆and⬇"、"⬆oor⬇"、"⬆not⬇"，"⬆⬇"表示是逻辑运算符，并对字段限定内部的空格给予删除，去除多余的空格，全部转小写后输出。
    public static String reFreeTxt(String str) {
        if (null == str || str.length() < 1) {
            return "";
        }
        str = str.replaceAll(" +", " ")
                .replace(" AND ", "⬆and⬇").replace(" OR ", "⬆oor⬇").replace(" NOT ", "⬆not⬇");
        str = str.toLowerCase()
                .replace("] and ", "]⬆and⬇").replace("] not ", "]⬆not⬇").replace("] or ", "]⬆oor⬇")
                .replace(") and ", ")⬆and⬇").replace(") not ", ")⬆not⬇").replace(") or ", ")⬆oor⬇")
                .replace(" and (", "⬆and⬇(").replace(" not (", "⬆not⬇(").replace(" or (", "⬆oor⬇(")
                .replace("\" and ", "\"⬆and⬇").replace("\" not ", "\"⬆not⬇").replace("\" or ", "\"⬆oor⬇")
                .replace(" and \"", "⬆and⬇\"").replace(" not \"", "⬆not⬇\"").replace(" or \"", "⬆oor⬇\"").trim();
        Matcher matcher1 = Pattern.compile("(\\[\\s*(c\\s*n\\s*a\\s*b|c\\s*n\\s*a\\s*d|c\\s*n\\s*a\\s*u|c\\s*n\\s*c\\s*a\\s*u|c\\s*n\\s*f\\s*a\\s*u|c\\s*n\\s*k\\s*w|c\\s*n\\s*t\\s*a|c\\s*n\\s*t\\s*i|c\\s*n\\s*t\\s*s|d\\s*e\\s*p\\s*t|d\\s*o\\s*i|d\\s*t" +
                "|e\\s*n\\s*a\\s*b|e\\s*n\\s*a\\s*d|e\\s*n\\s*a\\s*u|e\\s*n\\s*c\\s*a\\s*u|e\\s*n\\s*f\\s*a\\s*u|e\\s*n\\s*k\\s*w|e\\s*n\\s*t\\s*a|e\\s*n\\s*t\\s*i|e\\s*n\\s*t\\s*s|f\\s*u\\s*n\\s*d|i\\s*s\\s*s\\s*u\\s*e|l\\s*a|m\\s*e\\s*s\\s*h|m\\s*h" +
                "|p\\s*g|p\\s*m\\s*i\\s*d|p\\s*y|s\\s*h|s\\s*n|v\\s*l|t\\s*i|k\\s*w|a\\s*b|a\\s*d|f\\s*a\\s*u|c\\s*a\\s*u|e\\s*n\\s*u\\s*n\\s*i\\s*t|c\\s*n\\s*d\\s*e\\s*p\\s*t|e\\s*n\\s*d\\s*e\\s*p\\s*t|c\\s*n\\s*f\\s*u\\s*n\\s*d|e\\s*n\\s*f\\s*u\\s*n\\s*d" +
                "|c\\s*n\\s*u\\s*n\\s*i\\s*t|u\\s*n\\s*i\\s*t|t\\s*i\\s*a\\s*b|a\\s*u|t\\s*a|t\\s*s|i\\s*s)\\s*\\])").matcher(str);
        while (matcher1.find()) {
            str = str.replace(matcher1.group(), matcher1.group().replace(" ", "").replace("[", "⅝⋂").replace("]", "⨉ภ"));
        }
        str = str.replace("⅝⋂", "[").replace("⨉ภ", "]").trim();

        // 去除引号内部的括号、逻辑运算符。如果引号内部的括号、逻辑运算符不作替换，将导致按括号或逻辑运算计算，而引号的作用是强制精确运算，即加引号时，所有的括号、逻辑运算符统统不起作用。
        System.out.println("str:a " + str);
        Matcher matcher2 = Pattern.compile("\"([^\"]+)\"").matcher(str);
        while (matcher2.find()) {
            String group = matcher2.group(1);
            str = str.replace("\"" + group + "\"", "ㅑ" + group.replace("(", "⟪").replace(")", "⟫").replace("⬆and⬇", " and ").replace("⬆oor⬇", " or ").replace("⬆not⬇", " not ") + "ᅧ");
        }
        System.out.println("str:b " + str);
        return str.replace("ㅑ", "\"").replace("ᅧ", "\"").replaceAll(" +", " ").trim();
    }

    // 所有的字段限定集合
    public static Set<String> getFieldSet() {
        Set<String> set = new HashSet<>();
        set.add("[ti]");
        set.add("[tiab]");
        set.add("[kw]");
        set.add("[ts]");
        set.add("[ab]");
        set.add("[ad]");
        set.add("[au]");
        set.add("[ta]");
        set.add("[fau]");
        set.add("[cau]");
        set.add("[unit]");
        set.add("[jid]");
        set.add("[enunit]");
        set.add("[cnunit]");
        set.add("[cnab]");
        set.add("[cnad]");
        set.add("[cnau]");
        set.add("[cncau]");
        set.add("[cnfau]");
        set.add("[cnkw]");
        set.add("[cnta]");
        set.add("[cnti]");
        set.add("[cnts]");
        set.add("[dept]");
        set.add("[doi]");
        set.add("[dt]");
        set.add("[enab]");
        set.add("[enad]");
        set.add("[enau]");
        set.add("[encau]");
        set.add("[enfau]");
        set.add("[enkw]");
        set.add("[enta]");
        set.add("[enti]");
        set.add("[ents]");
        set.add("[fund]");
        set.add("[issue]");
        set.add("[is]");
        set.add("[la]");
        set.add("[mesh]");
        set.add("[mh]");
        set.add("[pg]");
        set.add("[pmid]");
        set.add("[py]");
        set.add("[sh]");
        set.add("[sn]");
        set.add("[vl]");
        set.add("[cndept]");
        set.add("[endept]");
        set.add("[cnfund]");
        set.add("[enfund]");
        return set;
    }

    // 获取标准的名称
    public static String getStandardName(String standard, String search) {
        System.out.println("----standard:1 " + standard + " search: " + search);
        if (standard.equals("☷")) {
            standard = search;
        }
        if (standard.equals("☷.")) {
            standard = search + ".";
        }
        if (standard.equals("☷,")) {
            standard = search + ",";
        }
        if (standard.equals("☷;")) {
            standard = search + ";";
        }
        if (standard.equals("☷?")) {
            standard = search + "?";
        }
        if (standard.equals("☷*")) {
            standard = search + "*";
        }
        if (standard.equals("☷(")) {
            standard = "(" + search + ").";
        }
        if (standard.equals("☷)")) {
            standard = "(" + search + ")";
        }
        if (standard.equals("☷[")) {
            standard = "[" + search + "].";
        }
        if (standard.equals("☷]")) {
            standard = "[" + search + "]";
        }
        if (standard.equals("ㅐ")) {
            standard = SubDataUtils.resetRemaining(search);
        }
        if (standard.equals("ㅐ.")) {
            standard = SubDataUtils.resetRemaining(search) + ".";
        }
        if (standard.equals("ㅐ,")) {
            standard = SubDataUtils.resetRemaining(search) + ",";
        }
        if (standard.equals("ㅐ;")) {
            standard = SubDataUtils.resetRemaining(search) + ";";
        }
        if (standard.equals("ㅐ?")) {
            standard = SubDataUtils.resetRemaining(search) + "?";
        }
        if (standard.equals("ㅐ*")) {
            standard = SubDataUtils.resetRemaining(search) + "*";
        }
        if (standard.equals("ㅐ(")) {
            standard = "(" + search + ").";
        }
        if (standard.equals("ㅐ)")) {
            standard = "(" + search + ")";
        }
        if (standard.equals("ㅐ[")) {
            standard = "[" + search + "].";
        }
        if (standard.equals("ㅐ]")) {
            standard = "[" + search + "]";
        }
        System.out.println("----standard:2 " + standard + " search: " + search);
        return standard;
    }

    // 因英文下是以空格切割的，如果某个单词后面是一个句号，因切割后单词与句号是连在一起的，就无法匹配出来了，为了解决这个问题，需要双方（加MAP及匹配时）均对句号等给转换一下，存MAP时，如果info与standard值相等，则只插入info，已节约空间。
    public static void putNameAndTypeMap(String info, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, String standard, String type, Map<String, String> md5Map) {
        if (standard.length() < 1) {
            standard = info;
        }
        info = reRemaining(info);
        standard = standard.toLowerCase().replaceAll(" +", " ").trim();
        if (info.equals(standard)) {
            standard = "☷";
        }
        if ((info + ".").equals(standard)) {
            standard = "☷.";
        }
        if ((info + ",").equals(standard)) {
            standard = "☷,";
        }
        if ((info + ";").equals(standard)) {
            standard = "☷;";
        }
        if ((info + "?").equals(standard)) {
            standard = "☷?";
        }
        if ((info + "*").equals(standard)) {
            standard = "☷*";
        }
        if (("(" + info + ").").equals(standard)) {
            standard = "☷(";
        }
        if (("(" + info + ")").equals(standard)) {
            standard = "☷)";
        }
        if (("[" + info + "].").equals(standard)) {
            standard = "☷[";
        }
        if (("[" + info + "]").equals(standard)) {
            standard = "☷]";
        }

        if (resetRemaining(info).equals(standard)) {
            standard = "ㅐ";
        }
        if ((resetRemaining(info) + ".").equals(standard)) {
            standard = "ㅐ.";
        }
        if ((resetRemaining(info) + ",").equals(standard)) {
            standard = "ㅐ,";
        }
        if ((resetRemaining(info) + ";").equals(standard)) {
            standard = "ㅐ;";
        }
        if ((resetRemaining(info) + "?").equals(standard)) {
            standard = "ㅐ?";
        }
        if ((resetRemaining(info) + "*").equals(standard)) {
            standard = "ㅐ*";
        }
        if (("(" + resetRemaining(info) + ").").equals(standard)) {
            standard = "ㅐ(";
        }
        if (("(" + resetRemaining(info) + ")").equals(standard)) {
            standard = "ㅐ)";
        }
        if (("[" + resetRemaining(info) + "].").equals(standard)) {
            standard = "ㅐ[";
        }
        if (("[" + resetRemaining(info) + "]").equals(standard)) {
            standard = "ㅐ]";
        }

        if (info.length() > 16) {
            String md5 = SecureUtil.md5(info).substring(8, 24).toLowerCase();
            md5Map.put(md5, info);
            info = md5;
        }
        if (info.length() > 1) {
            SubDataUtils.StandardNameAndType value = new SubDataUtils.StandardNameAndType();
            value.setStandard(standard);
            value.setType(type);
            nameAndTypeMap.compute(info, (k, v) -> {
                if (v == null) {
                    v = new ConcurrentHashSet<>();
                }
                v.add(value);
                return v;
            });
        }
    }

    // 对于加入NameAndTypeMap的，以及需要使用此进行匹配的，均执行相同的替换方法。为什么". "要替换成" 纃1 "，原因是在英文时，是以空格切割的，若某个单词与句号在一起，切割后仍然在一起，就导致无法匹配出来了，执行完匹配后，需要再次还原回来。
    public static String reRemaining(String info) {
        info = reEndStr(info); // Particle formation by supercritical fluid extraction and expansion process. 通过超临界流体萃取和膨胀过程形成颗粒
        info = info.toLowerCase().replaceAll(" +", " ").trim().replace("|", " 纃1 ").replace(". ", " 纃a ").replace(" .", " 纃b ").replace(".", " 纃c ")
                .replace(", ", " 纃d ").replace(" ,", " 纃e ").replace(",", " 纃f ").replace("; ", " 纃g ").replace(" ;", " 纃h ").replace(";", " 纃i ")
                .replace("( ", " 纃j ").replace(" (", " 纃k ").replace("(", " 纃l ").replace(") ", " 纃m ").replace(" )", " 纃n ").replace(")", " 纃o ")
                .replace("/ ", " 纃p ").replace(" /", " 纃q ").replace("/", " 纃r ").replace("\" ", " 纃s ").replace(" \"", " 纃t ").replace("\"", " 纃u ")
                .replace(": ", " 纃v ").replace(" :", " 纃w ").replace(":", " 纃x ").replace("?", " 纃y ").replace("*", " 纃z ").replaceAll(" +", " ").trim();
        return info.replace("⟪", "(").replace("⟫", ")");
    }

    public static String reEndStr(String info) {
        info = info.replaceAll(" +", " ").trim();
        if (info.endsWith(".")) {
            info = info.substring(0, info.length() - ".".length()).trim();
        }
        if (info.endsWith(",")) {
            info = info.substring(0, info.length() - ".".length()).trim();
        }
        if (info.endsWith(";")) {
            info = info.substring(0, info.length() - ".".length()).trim();
        }
        if (info.endsWith("?")) {
            info = info.substring(0, info.length() - "?".length()).trim();
        }
        if (info.endsWith("*")) {
            info = info.substring(0, info.length() - "*".length()).trim();
        }
        if (info.startsWith("(") && info.endsWith(").")) {
            info = info.substring("(".length(), info.length() - ").".length()).trim();
        }
        if (info.startsWith("(") && info.endsWith(")")) {
            info = info.substring("(".length(), info.length() - ")".length()).trim();
        }
        if (info.startsWith("[") && info.endsWith("].")) {
            info = info.substring("[".length(), info.length() - "].".length()).trim();
        }
        if (info.startsWith("[") && info.endsWith("]")) {
            info = info.substring("[".length(), info.length() - "]".length()).trim();
        }
        return info.replaceAll(" +", " ").trim();
    }

    // 返回余下的，因为在匹配之前对文本作了操作，返回之前需要再次还原回来。
    public static String resetRemaining(String remaining) {
        remaining = remaining.replaceAll(" +", " ").trim().replace(" 纃1 ", "|").replace(" 纃a ", ". ").replace(" 纃b ", " .").replace(" 纃c ", ".")
                .replace(" 纃d ", ", ").replace(" 纃e ", " ,").replace(" 纃f ", ",").replace(" 纃g ", "; ").replace(" 纃h ", " ;").replace(" 纃i ", ";")
                .replace(" 纃j ", "( ").replace(" 纃k ", " (").replace(" 纃l ", "(").replace(" 纃m ", ") ").replace(" 纃n ", " )").replace(" 纃o ", ")")
                .replace(" 纃p ", "/ ").replace(" 纃q ", " /").replace(" 纃r ", "/").replace(" 纃s ", "\" ").replace(" 纃t ", " \"").replace(" 纃u ", "\"")
                .replace(" 纃v ", ": ").replace(" 纃w ", " :").replace(" 纃x ", ":").replace(" 纃y ", "?").replace(" 纃z ", "*");
        remaining = remaining.replaceAll(" +", " ").trim().replace("纃a", " ").replace("纃b", " ").replace("纃c", " ").replace("纃d", " ").replace("纃e", " ")
                .replace("纃f", " ").replace("纃g", " ").replace("纃h", " ").replace("纃i", " ").replace("纃j", " ").replace("纃k", " ")
                .replace("纃l", " ").replace("纃m", " ").replace("纃n", " ").replace("纃o", " ").replace("纃p", " ").replace("纃q", " ")
                .replace("纃r", " ").replace("纃s", " ").replace("纃t", " ").replace("纃u", " ").replace("纃v", " ").replace("纃w", " ")
                .replace("纃x", " ").replace("纃y", " ").replace("纃z", " ").replace("纃1", " ").replaceAll(" +", " ").trim();
        return remaining;
    }

    // 对小于3位的英文单词或中文词组，认定为停用词，给予删除。
    public static String reStopWord(String info) {
        StringBuilder str = new StringBuilder();
        String[] list = info.split(" ");
        for (String s : list) {
            s = reEndStr(s);
            if (s.length() > 3) {
                str.append(s).append(" ");
            }
        }
        return str.toString().replaceAll(" +", " ").trim();
    }

    @Data
    public static class FieldAndValue {
        private String field = "";
        private String value = "";
    }

    @Data
    public static class StandardNameAndType {
        private String standard = "";
        private String type = "";
    }

    @Data
    public static class JourNameList {
        private List<String> cnNameList = new ArrayList<>();
        private List<String> enNameList = new ArrayList<>();
        private List<String> issnList = new ArrayList<>();
    }


}
